package com.bytemonster.serve;

import cn.hutool.extra.spring.SpringUtil;
import com.bytemonster.last.WsLast;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class WsServe {

    @Value("${websocket.server.port:8082}")
    private int serverPort;

    @Value("${websocket.idle.timeout:12}")
    private int idleTimeout;

    @Value("${websocket.max.content.length:65536}")
    private int maxContentLength;

    @Value("${websocket.path:/websocket}")
    private String websocketPath;

    @Autowired
    @Qualifier("networkExecutor")
    private Executor executor;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ChannelFuture serverChannelFuture;

    @PostConstruct
    public void start() {
        executor.execute(this::run);
    }

    private void run() {
        bossGroup = new NioEventLoopGroup(1); // 只需1个线程处理accept事件
        workerGroup = new NioEventLoopGroup(); // 默认CPU核心数*2

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new WebSocketChannelInitializer())
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true);

            serverChannelFuture = serverBootstrap.bind(serverPort).sync();
            serverChannelFuture.addListener(future -> {
                if (future.isSuccess()) {
                    log.info("Ws 启动成功: {}", serverPort);
                } else {
                    log.error("Ws 启动失败: {}", serverPort, future.cause());
                }
            });

            serverChannelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("Ws 服务器已中断", e);
        } catch (Exception e) {
            log.error("Ws 服务器遇到错误", e);
            throw new RuntimeException("无法启动 Ws 服务", e);
        }
    }

    @PreDestroy
    public void stop() {
        if (serverChannelFuture != null) {
            serverChannelFuture.channel().close();
        }
        if (bossGroup != null) {
            bossGroup.shutdownGracefully().syncUninterruptibly();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully().syncUninterruptibly();
        }
        log.info("Ws 服务停止!");
    }

    private class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) {
            ChannelPipeline pipeline = ch.pipeline();
            // HTTP编解码器
            pipeline.addLast(new HttpServerCodec());
            // 支持大数据流写入
            pipeline.addLast(new ChunkedWriteHandler());
            // 空闲连接检测(单位改为秒更合理)
            pipeline.addLast(new IdleStateHandler(idleTimeout, idleTimeout, idleTimeout, TimeUnit.SECONDS));
            // 聚合HTTP请求
            pipeline.addLast(new HttpObjectAggregator(maxContentLength));
            // 自定义业务处理器
            pipeline.addLast(SpringUtil.getBean(WsLast.class));
            // WebSocket协议处理器
            pipeline.addLast(new WebSocketServerProtocolHandler(websocketPath));
        }
    }
}